/******************************************************************
 * 个人博客：https://blog.csdn.net/2302_80277720
 * 嵌入式Linux学习交流群：1005210698
 * 欢迎各位大佬和萌新来加入交流学习
 * Change Logs:
 * Date           Author       Notes
 * 2025-08-24     喝呜昂黄    first version
 * 2025-08-30     喝呜昂黄    修改页翻转，添加日志功能
 ******************************************************************/

#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include "common.h"
#include "disp_manager.h"

static drmModeConnector *conn;        // 连接器的详细信息
static drmModeRes *res;               // DRM设备整体资源
static drmModePlaneRes *plane_res;    // 平面资源

static int fd;              // 文件描述符
static uint32_t conn_id;    // 选中的连接器ID
static uint32_t crtc_id;    // 选中的CRTC ID

static property_crtc_t property_crtc;    // CRTC相关属性ID的结构体

/**
 * 将子像素排列模式枚举值转换为对应的字符串描述
 *
 * @param sub_pixel: drmModeSubPixel类型的枚举值
 * @return: 指向子像素排列模式描述字符串的指针
 */
static char *get_sub_format(drmModeSubPixel sub_pixel) {
    switch (sub_pixel) {
        case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
            return "horizontal RGB";
        case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
            return "horizontal BGR";
        case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
            return "vertical RGB";
        case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
            return "vertical BGR";
        case DRM_MODE_SUBPIXEL_NONE:
            return "none";
        case DRM_MODE_SUBPIXEL_UNKNOWN:
        default:
            return "未知的";
    }
}

/**
 * 将连接器类型枚举值转换为对应的字符串描述
 *
 * @param type: 连接器类型枚举值
 * @return: 指向连接器类型描述字符串的指针
 */
static char *get_connector_type(uint32_t type) {
    switch (type) {
        case DRM_MODE_CONNECTOR_Unknown:
            return "未知的";
        case DRM_MODE_CONNECTOR_VGA:
            return "VGA";
        case DRM_MODE_CONNECTOR_DVII:
            return "DVI-I（双链路数字/模拟）";
        case DRM_MODE_CONNECTOR_DVID:
            return "DVI-D（双链路数字）";
        case DRM_MODE_CONNECTOR_DVIA:
            return "DVI-A（模拟）";
        case DRM_MODE_CONNECTOR_Composite:
            return "复合视频（CVBS）";
        case DRM_MODE_CONNECTOR_SVIDEO:
            return "S-视频（Y/C分离）";
        case DRM_MODE_CONNECTOR_LVDS:
            return "LVDS（低压差分信号）";
        case DRM_MODE_CONNECTOR_Component:
            return "分量视频（YPbPr/RGB）";
        case DRM_MODE_CONNECTOR_9PinDIN:
            return "9针DIN（SCART/RS-422）";
        case DRM_MODE_CONNECTOR_DisplayPort:
            return "DisplayPort（DP）";
        case DRM_MODE_CONNECTOR_HDMIA:
            return "HDMI-A（标准HDMI）";
        case DRM_MODE_CONNECTOR_HDMIB:
            return "HDMI-B（双链路HDMI，罕见）";
        case DRM_MODE_CONNECTOR_TV:
            return "电视（射频/AV端子）";
        case DRM_MODE_CONNECTOR_eDP:
            return "eDP";
        case DRM_MODE_CONNECTOR_VIRTUAL:
            return "虚拟连接器（无物理接口）";
        case DRM_MODE_CONNECTOR_DSI:
            return "DSI";
        case DRM_MODE_CONNECTOR_DPI:
            return "DPI";
        case DRM_MODE_CONNECTOR_WRITEBACK:
            return "回写连接器（用于帧缓冲捕获）";
        default:
            return "未定义的连接器类型";
    }
}

/**
 * 返回 DRM 的图像格式
 *
 * @param format: 自定以格式
 * @return: DRM 的图像格式
 */
static uint32_t get_drm_format(uint32_t format) {
    switch (format) {
        case IMAGE_FORMAT_RGB888:
            return DRM_FORMAT_RGB888;
        case IMAGE_FORMAT_BGR888:
            return DRM_FORMAT_BGR888;
        case IMAGE_FORMAT_ARGB8888:
            return DRM_FORMAT_ARGB8888;
        case IMAGE_FORMAT_ABGR8888:
            return DRM_FORMAT_ABGR8888;
        case IMAGE_FORMAT_XRGB8888:
            return DRM_FORMAT_XRGB8888;
        case IMAGE_FORMAT_XBGR8888:
            return DRM_FORMAT_XBGR8888;
        case IMAGE_FORMAT_YUV420SP_NV12:
            return DRM_FORMAT_NV12;
        default:
            return -1;
    }
}

/**
 * 将FourCC像素格式编码转换为对应的字符串表示
 *
 * @param fourcc: 32位的FourCC像素格式编码
 * @return: 指向表示该像素格式的字符串指针
 */
static char *fourcc_to_string(uint32_t fourcc) {
    static char buf[5];    // 使用静态缓冲区，避免内存泄漏和野指针问题

    // FourCC是4字节ASCII字符的小端存储，按字节提取并转换
    buf[0] = (fourcc >> 0) & 0xFF;     // 提取最低位字节
    buf[1] = (fourcc >> 8) & 0xFF;     // 提取次低位字节
    buf[2] = (fourcc >> 16) & 0xFF;    // 提取次高位字节
    buf[3] = (fourcc >> 24) & 0xFF;    // 提取最高位字节
    buf[4] = '\0';                     // 字符串终止符

    // 扩展常见格式的映射表
    switch (fourcc) {
        case DRM_FORMAT_ARGB8888:
            return "ARGB8888";
        case DRM_FORMAT_XRGB8888:
            return "XRGB8888";
        case DRM_FORMAT_XBGR8888:
            return "XBGR8888";
        case DRM_FORMAT_ABGR8888:
            return "ABGR8888";
        case DRM_FORMAT_RGB888:
            return "RGB888";
        case DRM_FORMAT_BGR888:
            return "BGR888";
        case DRM_FORMAT_RGB565:
            return "RGB565";
        case DRM_FORMAT_BGR565:
            return "BGR565";
        case DRM_FORMAT_YUYV:
            return "YUYV";
        case DRM_FORMAT_UYVY:
            return "UYVY";
        case DRM_FORMAT_NV12:
            return "NV12";
        case DRM_FORMAT_NV16:
            return "NV16";
        default:
            return buf;    // 未知格式返回原始字符组合
    }
}

/**
 * 创建DRM帧缓冲(FB)及相关缓冲区，并映射到用户空间
 *
 * @param drm_dev: 指向drm_device结构体的指针
 * @return: 0表示创建成功，-1表示过程中出现错误
 */
static int drm_create_fb(drm_device_t *drm_dev, uint32_t format) {
    int ret;
    // 1. 配置缓冲区参数
    drm_dev->create.width  = drm_dev->disp_image[0].width;
    drm_dev->create.height = drm_dev->disp_image[0].height;
    drm_dev->create.bpp    = drm_dev->disp_image[0].bpp;

    for (int i = 0; i < 2; i++) {
        // 2. 调用IOCTL创建缓冲区
        ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &drm_dev->create);
        if (ret < 0) {
            DISP_LOG_ERROR("创建缓冲区失败\n");
            return -1;
        }

        printf("== struct drm_mode_create_dumb[%d] 详情 ==\n", i);
        printf("输入参数:\n");
        printf("  width:  %u 像素\n", drm_dev->create.width);
        printf("  height: %u 像素\n", drm_dev->create.height);
        printf("  bpp:    %u 位/像素\n", drm_dev->create.bpp);
        printf("  flags:  %u\n", drm_dev->create.flags);
        printf("输出参数:\n");
        printf("  handle: %u\n", drm_dev->create.handle);
        printf("  pitch:  %u 字节(32对齐)\n", drm_dev->create.pitch);
        printf("  size:   %llu 字节\n", (unsigned long long)drm_dev->create.size);
        printf("======================================\n");

        // 3. 将缓冲区注册为帧缓冲
        uint32_t handles[2] = {0};
        uint32_t pitches[2] = {0};
        uint32_t offsets[2] = {0};

        drm_dev->disp_image[i].width_stride = drm_dev->create.pitch;     // 保存行跨度
        drm_dev->disp_image[i].size         = drm_dev->create.size;      // 保存总大小
        drm_dev->handle[i]                  = drm_dev->create.handle;    // 保存句柄

        // 支持多平面像素格式
        if (format == DRM_FORMAT_NV12) {
            // 泰山派摄像头获取的的 NV12 在一个平面内
            // 两个平面共用同一缓冲区句柄
            handles[0] = drm_dev->handle[i];
            handles[1] = drm_dev->handle[i];

            // Y平面行跨度为 drm_dev->pitch，UV 平面通常为其1/2（水平采样率1:2）
            pitches[0] = drm_dev->create.width;
            pitches[1] = drm_dev->create.width;

            // UV 平面偏移量为 Y 平面大小（width*height）
            offsets[0] = 0;
            offsets[1] = drm_dev->create.width * drm_dev->create.height;
            printf("1\n");
        } else if (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_ABGR8888 || format == DRM_FORMAT_XRGB8888 || format == DRM_FORMAT_XBGR8888 ||
                   format == DRM_FORMAT_RGB888 || format == DRM_FORMAT_BGR888) {
            handles[0] = drm_dev->handle[i];
            pitches[0] = drm_dev->disp_image[i].width_stride;
        } else {
            DISP_LOG_ERROR("%s format not supported", fourcc_to_string(format));
            return -1;
        }
        ret = drmModeAddFB2WithModifiers(
            fd, drm_dev->create.width, drm_dev->create.height, format, handles, pitches, offsets, NULL, &drm_dev->fd_ids[i], 0);
        if (ret < 0) {
            DISP_LOG_ERROR("添加FB[%d]失败, ret = %d", i, ret);
            return -1;
        }

        // 4. 映射缓冲区到用户空间
        drm_dev->map.handle = drm_dev->handle[i];
        ret                 = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &drm_dev->map);
        if (ret < 0) {
            DISP_LOG_ERROR("映射缓冲区失败");
            return -1;
        }

        printf("== struct drm_mode_map_dumb[%d] 详情 ==\n", i);
        printf("输入参数:\n");
        printf("  handle: %u\n", drm_dev->map.handle);
        printf("  填充位: %u\n", drm_dev->map.pad);
        printf("输出参数:\n");
        printf("  offset: 0x%llx\n", (unsigned long long)drm_dev->map.offset);
        printf("===================================\n");

        // 5. 通过mmap将缓冲区映射到用户空间
        drm_dev->disp_image[i].virt_addr = mmap(0, drm_dev->create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, drm_dev->map.offset);
        if (drm_dev->disp_image[i].virt_addr == MAP_FAILED) {
            DISP_LOG_ERROR("mmap 失败\n");
            return -1;
        }

        memset(drm_dev->disp_image[i].virt_addr, 0xff, drm_dev->disp_image[i].size);
    }
    return 0;
}

/**
 * 销毁DRM帧缓冲及相关资源
 *
 * @param drm_dev: 指向drm_device结构体的指针
 * @return: 无
 */
static void drm_destroy_fb(drm_device_t *drm_dev) {
    struct drm_mode_destroy_dumb destroy = {};

    for (int i = 0; i < 2; i++) {
        if (drm_dev->fd_ids[i] > 0) {
            drmModeRmFB(fd, drm_dev->fd_ids[i]);    // 移除帧缓冲
            drm_dev->fd_ids[i] = -1;
        }
        if (drm_dev->disp_image[i].virt_addr) {
            munmap(drm_dev->disp_image[i].virt_addr, drm_dev->disp_image[i].size);    // 解除用户空间映射
            drm_dev->disp_image[i].virt_addr = NULL;
        }
        if (drm_dev->handle[i] > 0) {
            destroy.handle = drm_dev->handle[i];
            drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);    // 销毁缓冲区
            drm_dev->handle[i] = -1;
        }
    }
}

/**
 * 打印DRM对象的属性信息
 *
 * @param props: 指向drmModeObjectProperties结构体的指针
 * @return: 无
 */
static void property_print(drmModeObjectProperties *props) {
    drmModePropertyPtr property;
    uint32_t i;

    printf("================ 对象属性（共%d个） ================\n", props->count_props);

    for (i = 0; i < props->count_props; i++) {
        property = drmModeGetProperty(fd, props->props[i]);
        if (!property)
            continue;

        printf("属性名: \"%s\"%*s| ID: %3u | 值: %8ld\n", property->name, (int)(18 - strlen(property->name)), "", props->props[i],
            (long)props->prop_values[i]);
        drmModeFreeProperty(property);
    }
    printf("====================================================\n");
}

/**
 * 获取指定DRM平面的属性ID并存储到平面属性数组中
 *
 * @param props: 指向drmModeObjectProperties结构体的指针
 * @param name: 想要查找的属性名
 * @return: 0表示成功获取并存储属性ID，-1表示获取属性列表失败
 */
static int get_property_id(int fd, drmModeObjectProperties *props, const char *name) {
    drmModePropertyPtr property;
    uint32_t i, id = 0;

    for (i = 0; i < props->count_props; i++) {
        property = drmModeGetProperty(fd, props->props[i]);
        if (!strcmp(property->name, name))    // 匹配属性名
            id = property->prop_id;           // 获取属性ID

        drmModeFreeProperty(property);
        if (id)
            break;    // 找到后退出循环
    }
    if (!id) {
        DISP_LOG_WARN("警告：未找到属性\"%s\"\n", name);
        return -1;
    }

    return id;
}

/**
 * 获取指定DRM平面的关键属性ID并存储
 *
 * @param property_planes: 指向 property_planes_t 结构体的指针
 * @param plane_id: 目标平面的ID，用于获取其属性信息
 * @return: 0表示成功获取并存储属性ID，-1表示获取属性失败
 */
int drm_get_plane_property(property_planes_t *property_planes, uint32_t plane_id) {
    drmModeObjectProperties *props;    // 平面的属性列表

    // 获取平面的所有属性
    props = drmModeObjectGetProperties(fd, plane_id, DRM_MODE_OBJECT_PLANE);
    if (!props) {
        DISP_LOG_ERROR("Failed to get plane properties");
        return -1;
    }

    // property_print(props);

    // 保存平面关键属性的ID（用于后续原子操作）
    property_planes->property_fb_id  = get_property_id(fd, props, "FB_ID");     // 绑定帧缓冲
    property_planes->property_crtc_x = get_property_id(fd, props, "CRTC_X");    // 显示位置X
    property_planes->property_crtc_y = get_property_id(fd, props, "CRTC_Y");    // 显示位置Y
    property_planes->property_crtc_w = get_property_id(fd, props, "CRTC_W");    // 显示宽度
    property_planes->property_crtc_h = get_property_id(fd, props, "CRTC_H");    // 显示高度
    property_planes->property_src_x  = get_property_id(fd, props, "SRC_X");     // 源图X
    property_planes->property_src_y  = get_property_id(fd, props, "SRC_Y");     // 源图Y
    property_planes->property_src_w  = get_property_id(fd, props, "SRC_W");     // 源图宽度
    property_planes->property_src_h  = get_property_id(fd, props, "SRC_H");     // 源图高度

    drmModeFreeObjectProperties(props);

    return 0;
}

/**
 * 使用原子模式设置DRM平面的属性配置
 *
 * @param drm_dev: 指向 drm_device_t 结构体的指针
 * @return: 0表示平面配置成功提交，-1表示分配请求失败或提交失败
 */
int drm_set_plane(drm_device_t *drm_dev) {
    int ret;
    drmModeAtomicReq *req;    // 原子请求结构体
    planes_setting_t planes_setting   = drm_dev->planes_setting;
    property_planes_t property_planes = drm_dev->property_planes;

    // 1. 分配原子请求结构体
    req = drmModeAtomicAlloc();
    if (!req) {
        DISP_LOG_ERROR("Failed to alloc atomic req\n");
        return -1;
    }

    // 2. 向原子请求中添加属性修改（所有修改将原子生效）
    // 平面关联CRTC
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_crtc.property_crtc_id, crtc_id);
    // 平面绑定帧缓冲（FB_ID属性）
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_fb_id, planes_setting.fb_id);
    // 设置平面在CRTC上的位置和大小
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_crtc_x, planes_setting.crtc_x);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_crtc_y, planes_setting.crtc_y);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_crtc_w, planes_setting.crtc_w);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_crtc_h, planes_setting.crtc_h);
    // // 设置从帧缓冲中取图的区域（SRC属性为16.16定点数，需左移16位）
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_src_x, planes_setting.src_x << 16);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_src_y, planes_setting.src_y << 16);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_src_w, planes_setting.src_w << 16);
    drmModeAtomicAddProperty(req, planes_setting.plane_id, property_planes.property_src_h, planes_setting.src_h << 16);

    // 3. 提交原子请求：所有属性修改原子生效
    ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_NONBLOCK, NULL);
    if (ret < 0) {
        DISP_LOG_ERROR("Failed to commit atomic req\n");
        drmModeAtomicFree(req);
        return -1;
    }

    // 4. 释放原子请求结构体
    drmModeAtomicFree(req);

    drmModeObjectProperties *props;    // 平面的属性列表

    // 获取平面的所有属性
    props = drmModeObjectGetProperties(fd, drm_dev->plane_id, DRM_MODE_OBJECT_PLANE);
    if (!props) {
        DISP_LOG_ERROR("Failed to get plane properties");
        return -1;
    }

    property_print(props);

    drmModeFreeObjectProperties(props);

    return 0;
}

/**
 * 使用原子模式设置 CRTC 的属性配置
 *
 * @return: 0表示 CRTC 配置成功提交，-1表示分配请求失败或提交失败
 */
static uint32_t drm_set_crtc(void) {
    int ret = 0;
    drmModeAtomicReq *req;

    req = drmModeAtomicAlloc();
    if (!req) {
        printf("Failed to alloc atomic req\n");
        return -1;
    }

    // 激活CRTC
    drmModeAtomicAddProperty(req, crtc_id, property_crtc.property_active, 1);
    // 设置CRTC的显示模式
    drmModeAtomicAddProperty(req, crtc_id, property_crtc.property_mode_id, property_crtc.blob_id);
    // 连接器关联CRTC
    drmModeAtomicAddProperty(req, conn_id, property_crtc.property_crtc_id, crtc_id);

    ret = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET | DRM_MODE_ATOMIC_NONBLOCK, NULL);
    if (ret < 0) {
        printf("Failed to commit atomic req\n");
        drmModeAtomicFree(req);
        return -1;
    }

    drmModeAtomicFree(req);

    return 0;
}

/**
 * 使用原子模式刷新平面
 *
 * @param drm_dev: 指向 drm_device_t 结构体的指针
 * @return: 0表示平面配置成功提交，-1表示分配请求失败或提交失败
 */
int drm_update_plane(drm_device_t *drm_dev) {
    int ret;
    drmModeAtomicReq *req = NULL;

    req = drmModeAtomicAlloc();
    if (!req) {
        printf("Failed to alloc atomic req\n");
        return -1;
    }

    drmModeAtomicAddProperty(req, drm_dev->planes_setting.plane_id, drm_dev->property_planes.property_fb_id, drm_dev->planes_setting.fb_id);

    ret = drmModeAtomicCommit(fd, req, DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, drm_dev);
    if (ret < 0) {
        printf("Failed to commit atomic req\n");
        drmModeAtomicFree(req);
        return -1;
    }

    drmModeAtomicFree(req);

    return 0;
}

/**
 * 当页翻转操作完成后，DRM会通过事件机制调用此函数。
 * 主要作用是更新当前显示的缓冲区索引，并标记翻转操作已完成，允许新的翻转请求。
 *
 * @param fd DRM设备文件描述符
 * @param sequence 事件序列号，用于同步多个事件的顺序
 * @param tv_sec 事件发生的秒数时间戳
 * @param tv_usec 事件发生的微秒数时间戳
 * @param user_data 用户自定义数据指针
 */
void drm_page_flip_handler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) {
    drm_device_t *drm_device = (drm_device_t *)user_data;

    // 关键：翻转完成后，更新当前显示索引（后台缓冲变为前台）
    drm_device->current_idx = 1 - drm_device->current_idx;

    // 标记翻转完成，允许下一帧请求
    drm_device->flip_pending = 0;
}

/**
 * 负责循环处理DRM设备产生的事件，
 *
 * @param arg 线程参数
 * @return void* 线程退出时的返回值，通常为NULL
 */
void *drm_event_thread_func(void *arg) {
    drm_device_t *drm_device = (drm_device_t *)arg;

    drmEventContext ev_ctx = {
        .version           = DRM_EVENT_CONTEXT_VERSION,
        .page_flip_handler = drm_page_flip_handler    // 绑定回调
    };

    // 非阻塞模式：设置DRM设备FD为非阻塞
    int flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);

    drm_device->drm_run = 1;
    while (drm_device->drm_run) {
        // 非阻塞处理事件
        drmHandleEvent(fd, &ev_ctx);
        usleep(500);
    }

    return NULL;
}

/**
 * 向DRM驱动发送页翻转请求，将显示内容从当前缓冲区切换到后台缓冲区。
 *
 * @param drm_dev 指向drm_device_t结构体的指针，包含DRM设备的相关信息
 * @return int 成功返回0，失败返回-1
 */
int drm_request_page_flip(drm_device_t *drm_dev) {
    // 1. 检查是否有未完成的翻转（避免重复请求）
    if (drm_dev->flip_pending) {
        fprintf(stderr, "已有页翻转在等待，跳过请求\n");
        return -1;
    }

    drm_update_plane(drm_dev);

    // 4. 标记“翻转中”
    drm_dev->flip_pending = 1;

    return 0;
}

/**
 * 从设备所有平面中筛选出同时支持指定像素格式且可关联目标CRTC的平面
 *
 * @param crtc_id: 目标CRTC的索引，用于判断平面是否支持该CRTC
 * @param drm_dev: 指向 drm_device_t 结构体的指针
 * @return: 0表示成功找到找到符合条件的平面，-1表示未找到或出错
 */
static int find_plane(uint32_t crtc_id, drm_device_t *drm_dev) {
    int ret = 0;
    uint32_t i, j;
    uint32_t format;
    drmModePlanePtr plane;    // 单个平面的详细信息结构体

    format = drm_dev->format;

    // 1. 获取设备上所有平面的资源列表（包含平面ID和数量）
    plane_res = drmModeGetPlaneResources(fd);
    if (!plane_res) {
        DISP_LOG_ERROR("drmModeGetPlaneResources failed\n");
        return -1;
    }

    printf("\n========= drmModePlaneRes（平面资源） =========\n");
    printf("平面数量: %d\n", plane_res->count_planes);

    // 2. 遍历所有平面，筛选符合条件的平面
    for (i = 0; i < plane_res->count_planes; ++i) {
        if (i == 1)
            continue;

        uint32_t current_plane_id = plane_res->planes[i];    // 当前遍历的平面ID

        // 2.1 获取当前平面的详细信息（支持的格式、CRTC等）
        plane = drmModeGetPlane(fd, current_plane_id);
        if (!plane) {
            DISP_LOG_ERROR("drmModeGetPlane failed for plane %u\n", current_plane_id);
            break;
        }

        printf("-------- 平面 [%u]（ID: %u） --------\n", i, plane->plane_id);
        printf("当前绑定的CRTC ID: %u（0表示未绑定）\n", plane->crtc_id);
        printf("当前绑定的帧缓冲ID: %u（0表示未绑定）\n", plane->fb_id);
        printf("支持的CRTC掩码: 0x%x\n", plane->possible_crtcs);
        printf("目标CRTC索引: %u\n", crtc_id);
        printf("支持的像素格式数量: %u\n", plane->count_formats);
        for (j = 0; j < plane->count_formats; ++j)
            printf("  格式[%u]: %s %s\n", j, fourcc_to_string(plane->formats[j]), (plane->formats[j] == format) ? "(匹配目标格式)" : "");
        printf("伽马校正表大小: %u（0表示不支持）\n", plane->gamma_size);

        // 3. 条件1：检查平面是否支持目标CRTC（基于CRTC索引的位掩码判断）
        // possible_crtcs是位掩码，第n位为1表示支持索引为n的CRTC
        if (!(plane->possible_crtcs & (1 << crtc_id))) {
            DISP_LOG_WARN("筛选结果：不支持目标CRTC（索引: %u, ID: %u），跳过\n", crtc_id, crtc_id);
            drmModeFreePlane(plane);
            continue;
        }

        // 4. 条件2：检查平面是否支持目标像素格式
        for (j = 0; j < plane->count_formats; ++j) {
            if (plane->formats[j] == format)
                break;
        }

        // 若遍历完所有格式仍未找到目标格式，则跳过当前平面
        if (j == plane->count_formats) {
            DISP_LOG_WARN("筛选结果：不支持目标像素格式（0x%x），跳过\n\n", format);
            drmModeFreePlane(plane);
            continue;
        }

        // 5. 找到符合条件的平面，记录其ID
        drm_dev->plane_id = plane->plane_id;
        printf("\n筛选结果：找到符合条件的平面！ID: %u\n", drm_dev->plane_id);
        drmModeFreePlane(plane);

        break;
    }

    // 6. 检查是否找到有效平面
    if (i == plane_res->count_planes) {
        // 遍历完所有平面仍未找到，返回错误
        DISP_LOG_WARN("未找到符合条件的平面（格式: 0x%x, CRTC索引: %u）\n", format, crtc_id);
        ret = -1;
    }
    printf("===============================================\n");

    // 7. 释放平面资源结构体
    drmModeFreePlaneResources(plane_res);

    return ret;
}

/**
 * 打印DRM设备的资源信息详情
 *
 * @param 无
 * @return 无
 */
static void drmModeRes_print(void) {
    printf("\n===== DRM设备资源详情 (drmModeRes) =====\n");
    printf("帧缓冲(FB)信息:\n");
    printf("  数量: %d\n", res->count_fbs);
    if (res->count_fbs > 0 && res->fbs) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_fbs; i++) printf("%u ", res->fbs[i]);
        printf("\n");
    }
    printf("\nCRTC(显示控制器)信息:\n");
    printf("  数量: %d\n", res->count_crtcs);
    if (res->count_crtcs > 0 && res->crtcs) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_crtcs; i++) printf("%u ", res->crtcs[i]);
        printf("\n");
    }
    printf("\n连接器(Connector)信息:\n");
    printf("  数量: %d\n", res->count_connectors);
    if (res->count_connectors > 0 && res->connectors) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_connectors; i++) printf("%u ", res->connectors[i]);
        printf("\n");
    }
    printf("\n编码器(Encoder)信息:\n");
    printf("  数量: %d\n", res->count_encoders);
    if (res->count_encoders > 0 && res->encoders) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_encoders; i++) printf("%u ", res->encoders[i]);
        printf("\n");
    }
    printf("\n支持的显示尺寸范围:\n");
    printf("  宽度: %u (最小) ~ %u (最大) 像素\n", res->min_width, res->max_width);
    printf("  高度: %u (最小) ~ %u (最大) 像素\n", res->min_height, res->max_height);
    printf("======================================\n");
}

/**
 * 打印DRM模式连接器(connector)的详细资源信息
 *
 * @param 无
 * @return 无
 */
static void drmModeConnector_print(void) {
    printf("\n== 连接器资源详情 (drmModeConnector) ==\n");
    printf("连接器信息 (ID: %u)\n", conn->connector_id);
    printf("类型: %s (ID: %u)\n", get_connector_type(conn->connector_type), conn->connector_type_id);
    printf("连接状态: %s\n", conn->connection == DRM_MODE_CONNECTED ? "已连接" : "未连接");
    printf("显示器尺寸: %ux%u mm\n", conn->mmWidth, conn->mmHeight);
    printf("子像素排列: %s\n", get_sub_format(conn->subpixel));
    printf("支持的显示模式数量: %d\n", conn->count_modes);
    if (conn->count_modes > 0) {
        printf("  模式列表（分辨率@刷新率）:\n");
        for (int i = 0; i < conn->count_modes; i++)
            printf("  %d: %ux%u @ %dHz %s\n", i, conn->modes[i].hdisplay, conn->modes[i].vdisplay, conn->modes[i].vrefresh,
                (conn->modes[i].flags & DRM_MODE_TYPE_PREFERRED) ? "(首选)" : "");
    }
    printf("\n可关联的编码器数量: %d\n", conn->count_encoders);
    if (conn->count_encoders > 0) {
        printf("  编码器ID列表: ");
        for (int i = 0; i < conn->count_encoders; i++) printf("%u \n", conn->encoders[i]);
    }
    printf("\n当前关联的编码器ID: %u\n", conn->encoder_id);
    printf("======================================\n\n");
}

/**
 * 初始化DRM设备，完成显示相关资源的配置（原子模式）
 *
 * @param drm_device 指向 drm_device_t 结构体的指针
 * @return 0表示初始化成功，-1表示初始化失败
 */
int drm_init(drm_device_t *drm_device) {
    int ret = 0;

    drmModeAtomicReq *req;
    drmModeObjectProperties *props;

    // 1. 打开DRM设备
    fd = drmOpen("rockchip", NULL);
    if (fd < 0) {
        DISP_LOG_ERROR("打开 %s 设备失败\n", "rockchip");
        return -1;
    }

    // 2. 获取DRM设备整体资源（CRTC、连接器、编码器列表）
    res = drmModeGetResources(fd);
    if (!res) {
        DISP_LOG_ERROR("获取 DRM 资源失败");
        goto err;
    }
    drmModeRes_print();

    // 3. 选择第一个CRTC和连接器
    crtc_id = res->crtcs[0];
    conn_id = res->connectors[0];

    // 4. 开启通用平面支持
    drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);

    // 5. 根据图像格式寻找平面
    drm_device->format = DRM_FORMAT_BGR888;
    // drm_device->format = get_drm_format(drm_device->disp_image->format);

    ret = find_plane(0, drm_device);
    if (ret < 0)
        goto err1;

    // 7. 获取连接器详细信息（显示模式、连接状态等）
    conn = drmModeGetConnector(fd, conn_id);
    if (!conn) {
        DISP_LOG_ERROR("获取连接器失败\n");
        goto err2;
    }
    drmModeConnector_print();

    // 9. 创建帧缓冲
    drm_device->width  = conn->modes[0].hdisplay;
    drm_device->height = conn->modes[0].vdisplay;
    ret                = drm_create_fb(drm_device, drm_device->format);
    if (ret < 0)
        goto err3;

    // 10. 开启原子模式支持
    drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);

    // 11. 获取连接器属性：主要获取"CRTC_ID"属性的ID（用于关联CRTC）
    props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
    if (!props) {
        DISP_LOG_ERROR("获取连接器属性失败\n");
        goto err4;
    }
    // printf("连接器属性:\n");
    // property_print(props);
    property_crtc.property_crtc_id = get_property_id(fd, props, "CRTC_ID");    // 连接器关联CRTC的属性ID
    drmModeFreeObjectProperties(props);

    // 12. 获取CRTC属性："ACTIVE"（激活）和"MODE_ID"（显示模式）
    props = drmModeObjectGetProperties(fd, crtc_id, DRM_MODE_OBJECT_CRTC);
    if (!props) {
        DISP_LOG_ERROR("获取CRTC属性失败\n");
        goto err5;
    }
    // printf("CRTC 属性:\n");
    // property_print(props);
    property_crtc.property_active  = get_property_id(fd, props, "ACTIVE");     // 激活CRTC的属性ID
    property_crtc.property_mode_id = get_property_id(fd, props, "MODE_ID");    // 设置模式的属性ID
    drmModeFreeObjectProperties(props);

    // 13. 创建显示模式的blob：将显示模式封装为blob，供CRTC设置使用
    ret = drmModeCreatePropertyBlob(fd, &conn->modes[0], sizeof(conn->modes[0]), &property_crtc.blob_id);
    if (ret < 0) {
        DISP_LOG_ERROR("创建模式blob失败\n");
        goto err5;
    }

    // 14. 原子模式初始化CRTC和连接器：激活CRTC、设置显示模式、连接器关联CRTC
    req = drmModeAtomicAlloc();    // 分配原子请求
    if (!req) {
        DISP_LOG_ERROR("分配原子请求失败\n");
        goto err6;
    }

    ret = drm_set_crtc();
    if (ret < 0)
        goto err6;

    return 0;

err6:
    drmModeDestroyPropertyBlob(fd, property_crtc.blob_id);
err5:
    drmModeFreeObjectProperties(props);
err4:
    drm_destroy_fb(drm_device);
err3:
    drmModeFreeConnector(conn);
err2:
    drmModeFreePlaneResources(plane_res);
err1:
    drmModeFreeResources(res);
err:
    close(fd);

    return -1;
}

/**
 * 释放DRM相关的所有资源并退出
 *
 * @param drm_device 指向 drm_device_t 结构体的指针
 * @return: 无
 */
void drm_exit(drm_device_t *drm_device) {

    if (drm_device)
        drm_destroy_fb(drm_device);    // 销毁帧缓冲
    if (conn) {
        drmModeFreeConnector(conn);    // 释放连接器信息
        conn = NULL;
    }
    if (res) {
        drmModeFreeResources(res);    // 释放设备资源
        res = NULL;
    }
    if (fd >= 0) {
        close(fd);    // 关闭设备文件描述符
        fd = -1;
    }
}